#include "stdafx.h"

#include "..\TestsTreeView.h"
using namespace TddGui;

#include "DummyDialog.h"
#include "MessageBoxDismisser.h"

namespace Slow
{
TESTCLASS(TreeViewTests)
{
	struct StaticTextNotification : public TextNotification
	{
		CString m_cs;
		virtual void SetText(const CString& cs) { m_cs = cs; }
	} staticText, errorText;

	struct MockEnableNotification : public EnableNotification
	{
		bool m_b;
		virtual void Enable(bool b) { m_b = b; }
	} enableMock;

	struct MockProgressNotification : public ProgressNotification
	{
		unsigned int m_num, m_den;
		COLORREF m_color;
		virtual void Status(unsigned int numerator, unsigned int denominator, COLORREF color)
		{
			m_num = numerator;
			m_den = denominator;
			m_color = color;
		}
	} progressMock;

	virtual void TestInitialize()
	{
		staticText.m_cs = L"";
		errorText.m_cs = L"";
		enableMock.m_b = false;
		progressMock.m_num = progressMock.m_den = (unsigned int)-1;
		progressMock.m_color = RGB(0,0,0);
	}

	TESTMETHOD(EmptyTestListTest)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& , TDD::Reporter& )
			{
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// with a good test dll, the dll name is in the static text
		TddAssert().AreEqual(L"foo.dll", staticText.m_cs);

		// and the Run button is enabled
		TddAssert().IsTrue(enableMock.m_b);

		// the first node has "All Tests" as the text
		CComBSTR bstr;
		ttv.GetItemText(ttv.GetRootItem(), bstr.m_str);
		TddAssert().AreEqual(L"All Tests", bstr.m_str);

		// and the image is grey
		int nImage=-1, nSelectedImage=-1;
		ttv.GetItemImage(ttv.GetRootItem(), nImage, nSelectedImage);
		TddAssert().AreEqual(0,         nImage, L"expected grey (==0)");
		TddAssert().AreEqual(0, nSelectedImage, L"expected grey (==0)");

		// and there's only one node
		TddAssert().AreEqual(1, ttv.GetCount());
	}


	template<DWORD D> CString TestBadDlls()
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return D; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& , TDD::Reporter& ) { return S_OK; }
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		CString text, caption, okButton;
		{
			ExamineMessageBox emb(text, caption, okButton, dlg, IDOK); // fires up thread in ctor, polls for one second
			LoadNotification& ln = ttv;
			ln.LoadTests(CPath(L"?"));
		}
		TddAssert().AreEqual(L"TddGui", caption);
		TddAssert().AreEqual(L"OK", okButton);

		TddAssert().AreEqual(0, ttv.GetCount());
		return text;
	}
	TESTMETHOD(AccessDeniedOnDllNameMeansNoNodesInTreeView)
	{
		TddAssert().AreEqual(L"error loading ?: Access is denied.", TestBadDlls<E_ACCESSDENIED>());
	}
	TESTMETHOD(E_FAILMeansNoTestsInTestDll)
	{
		TddAssert().AreEqual(L"no tests found in ?", TestBadDlls<E_FAIL>());
	}


	void ExpectingGreyImages(CTreeViewCtrlEx& ttv, HTREEITEM item)
	{
		int nImage=-1, nSelectedImage=-1;
		ttv.GetItemImage(item, nImage, nSelectedImage);
		TddAssert().AreEqual(0,         nImage, L"expected grey (==0)");
		TddAssert().AreEqual(0, nSelectedImage, L"expected grey (==0)");
	}
	HTREEITEM VerifyNode(const CString& cs, HTREEITEM item, CTreeViewCtrlEx& ttv)
	{
		CComBSTR bstr;
		ttv.GetItemText(item, bstr.m_str);
		TddAssert().AreEqual(cs, bstr.m_str);
		ExpectingGreyImages(ttv, item);
		return ttv.GetChildItem(item);
	}
	TESTMETHOD(SimpleClassNameAndMethodNameMakes3Nodes)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				TDD::UnitTestInfo uti("class", "method");
				pred.WantTest(uti);				
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// there are 3 nodes:  "All Tests", "class" and "method", and they're all grey
		TddAssert().AreEqual(3, ttv.GetCount());
		HTREEITEM item = VerifyNode(L"All Tests", ttv.GetRootItem(), ttv);
		item = VerifyNode(L"class", item, ttv);
		item = VerifyNode(L"method", item, ttv);
	}
	TESTMETHOD(TwoNamespacesGenerates5Nodes)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				TDD::UnitTestInfo uti("namespace1::namespace2::class", "method");
				pred.WantTest(uti);				
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// there are 5 nodes:  "All Tests", "namespace1", "namespace2", "class" and "method", and they're all grey
		TddAssert().AreEqual(5, ttv.GetCount());

		HTREEITEM item = VerifyNode(L"All Tests", ttv.GetRootItem(), ttv);
		item = VerifyNode(L"namespace1", item, ttv);
		item = VerifyNode(L"namespace2", item, ttv);
		item = VerifyNode(L"class", item, ttv);
		item = VerifyNode(L"method", item, ttv);
	}

	TESTMETHOD(ThreeMethodsOnSameClassAndNamespacesImplies7Nodes)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				pred.WantTest(TDD::UnitTestInfo("namespace1::namespace2::class", "method1"));
				pred.WantTest(TDD::UnitTestInfo("namespace1::namespace2::class", "method2"));
				pred.WantTest(TDD::UnitTestInfo("namespace1::namespace2::class", "method3"));
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// there are 7 nodes
		TddAssert().AreEqual(7, ttv.GetCount());

		HTREEITEM item = VerifyNode(L"All Tests", ttv.GetRootItem(), ttv);
		item = VerifyNode(L"namespace1", item, ttv);
		item = VerifyNode(L"namespace2", item, ttv);
		item = VerifyNode(L"class", item, ttv);
		VerifyNode(L"method1", item, ttv);
		VerifyNode(L"method2", ttv.GetNextSiblingItem(item), ttv);
		VerifyNode(L"method3", ttv.GetNextSiblingItem(ttv.GetNextSiblingItem(item)), ttv);
	}
	TESTMETHOD(SameClassInTwoNamespacesGenerates7Nodes)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				pred.WantTest(TDD::UnitTestInfo("namespace1::class", "method"));
				pred.WantTest(TDD::UnitTestInfo("namespace2::class", "method"));
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// there are 7 nodes
		TddAssert().AreEqual(7, ttv.GetCount());

		HTREEITEM item = VerifyNode(L"All Tests", ttv.GetRootItem(), ttv);
		item = VerifyNode(L"namespace1", item, ttv);
		item = VerifyNode(L"class", item, ttv);
		item = VerifyNode(L"method", item, ttv);

		item = VerifyNode(L"namespace2", ttv.GetNextSiblingItem(ttv.GetChildItem(ttv.GetRootItem())), ttv);
		item = VerifyNode(L"class", item, ttv);
		item = VerifyNode(L"method", item, ttv);
	}

	TESTMETHOD(NotFindingFormerlySelectedTestWhenReloadingDllGeneratesMessageBox)
	{
		static bool first = true;
		first = true;

		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				if (first == true)
				{
					pred.WantTest(TDD::UnitTestInfo("class", "method1"));
					first = false;
				}
				else
				{
					pred.WantTest(TDD::UnitTestInfo("class", "method2"));
				}
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select the test
		ttv.GetRootItem().GetChild().GetChild().Select();

		CString text, caption, okButton;
		{
			ExamineMessageBox emb(text, caption, okButton, dlg, IDOK); // fires up thread in ctor, polls for one second
			RunNotification& rn = ttv;
			rn.Run();
		}
		TddAssert().AreEqual(L"can no longer find selected test(s)", text);
		TddAssert().AreEqual(L"TddGui", caption);
		TddAssert().AreEqual(L"OK", okButton);
	}

	TESTMETHOD(DeselectAnyNodesImpliesMessageBox)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& )
			{
				pred.WantTest(TDD::UnitTestInfo("class", "method"));
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// de-select any node
		ttv.SelectItem(NULL);

		CString text, caption, okButton;
		{
			ExamineMessageBox emb(text, caption, okButton, dlg, IDOK); // fires up thread in ctor, polls for one second
			RunNotification& rn = ttv;
			rn.Run();
		}
		TddAssert().AreEqual(L"no test(s) selected!", text);
		TddAssert().AreEqual(L"TddGui", caption);
		TddAssert().AreEqual(L"OK", okButton);
	}

	TESTMETHOD(RunOneTestFromRootMeansStatusBarHasOneIncrement)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				if (pred.WantTest(TDD::UnitTestInfo("class", "method")))
					func.ForEachTest(TDD::UnitTestInfo("class", "method"));
				return S_OK;
			}
		};

		struct MockProgressNotification : public ProgressNotification
		{
			struct Pair
			{
				unsigned int num, den;
			} m_pairs[2];
			unsigned int m_calls;
			MockProgressNotification() : m_calls(0) {}
			virtual void Status(unsigned int numerator, unsigned int denominator, COLORREF)
			{
				TddAssert().IsTrue(m_calls < 2);
				m_pairs[m_calls].num = numerator;
				m_pairs[m_calls].den = denominator;
				m_calls++;
			}
		} progressMock;

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		ttv.GetRootItem().Select();
		RunNotification& rn = ttv;
		rn.Run();

		TddAssert().AreEqual(  2, progressMock.m_calls);
		TddAssert().AreEqual(  0, progressMock.m_pairs[0].num);
		TddAssert().AreEqual(100, progressMock.m_pairs[0].den);
		TddAssert().AreEqual(  1, progressMock.m_pairs[1].num);
		TddAssert().AreEqual(  1, progressMock.m_pairs[1].den);

		// assert that all images are green
		int nImage=-1, nSelectedImage=-1;
		ttv.GetItemImage(ttv.GetRootItem(), nImage, nSelectedImage);
		TddAssert().AreEqual(1,         nImage, L"expected green (==1)");
		TddAssert().AreEqual(1, nSelectedImage, L"expected green (==1)");
		ttv.GetItemImage(ttv.GetRootItem().GetChild(), nImage, nSelectedImage);
		TddAssert().AreEqual(1,         nImage, L"expected green (==1)");
		TddAssert().AreEqual(1, nSelectedImage, L"expected green (==1)");
		ttv.GetItemImage(ttv.GetRootItem().GetChild().GetChild(), nImage, nSelectedImage);
		TddAssert().AreEqual(1,         nImage, L"expected green (==1)");
		TddAssert().AreEqual(1, nSelectedImage, L"expected green (==1)");

		TddAssert().AreEqual(L"", errorText.m_cs); // no error => no error text
	}

	TESTMETHOD(FailOneTestFromMethodNodeMeansErrorTextIsSetAndImagesAreRed)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				TDD::UnitTestInfo uti("class", "method1");
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
					func.ForEachFailure(TDD::TestFailure(&uti, 123, "lllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllong-path\\file.cpp", "my error"));
				}
				return S_OK;
			}
		};

		struct MockProgressNotification : public ProgressNotification
		{
			unsigned int m_calls;
			MockProgressNotification() : m_calls(0) {}
			virtual void Status(unsigned int , unsigned int , COLORREF)
			{
				m_calls++;
			}
		} progressMock;

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select method node
		ttv.GetRootItem().GetChild().GetChild().Select();
		RunNotification& rn = ttv;
		rn.Run();

		// 3 calls to progress:  one to reset, one for regular progress, one to set the color to red
		TddAssert().AreEqual(3, progressMock.m_calls);

		TddAssert().AreEqual(L"...lllllllllllllllllllllllllllllllllllong-path\\file.cpp\r\non line 123:\r\nmy error", errorText.m_cs);
		// all node images should be red
		int nImage=-1, nSelectedImage=-1;
		ttv.GetItemImage(ttv.GetRootItem(), nImage, nSelectedImage);
		TddAssert().AreEqual(2,         nImage, L"expected red (==2)");
		TddAssert().AreEqual(2, nSelectedImage, L"expected red (==2)");
		ttv.GetItemImage(ttv.GetRootItem().GetChild(), nImage, nSelectedImage);
		TddAssert().AreEqual(2,         nImage, L"expected red (==2)");
		TddAssert().AreEqual(2, nSelectedImage, L"expected red (==2)");
		ttv.GetItemImage(ttv.GetRootItem().GetChild().GetChild(), nImage, nSelectedImage);
		TddAssert().AreEqual(2,         nImage, L"expected red (==2)");
		TddAssert().AreEqual(2, nSelectedImage, L"expected red (==2)");
	}

	TESTMETHOD(PickingOneTestOfTwoRunsTheRightOne)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				RunTest(pred, func, "class", "method1");
				RunTest(pred, func, "class", "method2");
				return S_OK;
			}			
		private:
			static void RunTest(TDD::Discriminator& pred, TDD::Reporter& func, const char* className, const char* methodName)
			{
				TDD::UnitTestInfo uti(className, methodName);
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
				}
			}
		};

		struct MockProgressNotification : public ProgressNotification
		{
			unsigned int m_calls;
			MockProgressNotification() : m_calls(0) {}
			virtual void Status(unsigned int , unsigned int , COLORREF)
			{
				m_calls++;
			}
		} progressMock;

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select method1 node
		ttv.GetRootItem().GetChild().GetChild().Select();
		RunNotification& rn = ttv;
		rn.Run();

		TddAssert().AreEqual(2, progressMock.m_calls); // one for resetting back to zero, one for the one test.
	}

	TESTMETHOD(TwoTestsWhereOneMethodNameIsASubsetOfTheOtherAndSelectingTheShorterOneDoesNotRunBoth)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				RunTest(pred, func, "class", "MsiAttributes");
				RunTest(pred, func, "class", "MsiAttributesUsingDefault");
				return S_OK;
			}			
		private:
			static void RunTest(TDD::Discriminator& pred, TDD::Reporter& func, const char* className, const char* methodName)
			{
				TDD::UnitTestInfo uti(className, methodName);
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
				}
			}
		};

		struct MockProgressNotification : public ProgressNotification
		{
			unsigned int m_calls;
			MockProgressNotification() : m_calls(0) {}
			virtual void Status(unsigned int , unsigned int , COLORREF)
			{
				m_calls++;
			}
		} progressMock;

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select "MsiAttributes" (the shorter name) node
		ttv.GetRootItem().GetChild().GetChild().Select();
		RunNotification& rn = ttv;
		rn.Run();

		// only one test should be run
		TddAssert().AreEqual(2, progressMock.m_calls); // one for resetting back to zero, one for the one test.
	}
	TESTMETHOD(TwoTestsWhereOneClassOrNamespaceNameIsASubsetOfTheOtherAndSelectingTheShorterOneDoesNotRunBoth)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				RunTest(pred, func, "Foo",    "MsiAttributes");
				RunTest(pred, func, "FooBar", "MsiAttributes");
				return S_OK;
			}			
		private:
			static void RunTest(TDD::Discriminator& pred, TDD::Reporter& func, const char* className, const char* methodName)
			{
				TDD::UnitTestInfo uti(className, methodName);
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
				}
			}
		};

		struct MockProgressNotification : public ProgressNotification
		{
			unsigned int m_calls;
			MockProgressNotification() : m_calls(0) {}
			virtual void Status(unsigned int , unsigned int , COLORREF)
			{
				m_calls++;
			}
		} progressMock;

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select first namespace/class (the shorter name) node
		ttv.GetRootItem().GetChild().Select();
		RunNotification& rn = ttv;
		rn.Run();

		// only one test should be run
		TddAssert().AreEqual(2, progressMock.m_calls); // one for resetting back to zero, one for the one test.
	}

	TESTMETHOD(OnSuccessSetAllAncestorNodesToGreenUnlessItsNotGrey)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				{ // failing test
					TDD::UnitTestInfo uti("class", "method1");
					if (true == pred.WantTest(uti))
					{
						func.ForEachTest(uti);
						func.ForEachFailure(TDD::TestFailure(&uti, 123, "long-path\\file.cpp", "some error"));
					}
				}
				{ // succeeding test
					TDD::UnitTestInfo uti("class", "method2");
					if (true == pred.WantTest(uti))
					{
						func.ForEachTest(uti);
					}
				}
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select all tests
		ttv.GetRootItem().Select();
		RunNotification& rn = ttv;
		rn.Run();

		/* should have
		   All Tests (red)
		    |
			+- class (red)
			     |
				 +- method1 (red)
				 |
				 +- method2 (green)
		*/

		VerifyImages(ttv, ttv.GetRootItem(),                                        2, L"expecting red (==2)");
		VerifyImages(ttv, ttv.GetRootItem().GetChild(),                             2, L"expecting red (==2)");
		VerifyImages(ttv, ttv.GetRootItem().GetChild().GetChild(),                  2, L"expecting red (==2)");
		VerifyImages(ttv, ttv.GetRootItem().GetChild().GetChild().GetNextSibling(), 1, L"expecting green (==1)");
	}
	void VerifyImages(CTreeViewCtrlEx& ttv, HTREEITEM item, int color, LPCTSTR message)
	{
		int nImage=-1, nSelectedImage=-1;
		ttv.GetItemImage(item, nImage, nSelectedImage);
		TddAssert().AreEqual(color,         nImage, message);
		TddAssert().AreEqual(color, nSelectedImage, message);
	}

	TESTMETHOD(NoFailuresImpliesGreenBar)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				TDD::UnitTestInfo uti("class", "method");
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
				}
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select all tests
		ttv.GetRootItem().Select();
		RunNotification& rn = ttv;
		rn.Run();

		TddAssert().AreEqual(RGB(0x00, 0xFF, 0x00), progressMock.m_color);
	}
	TESTMETHOD(OneFailureImpliesRedBar)
	{
		struct TestRunner
		{
			static TDD::Reporter& GetNullReporter() { return TddGui::TestRunner::GetNullReporter(); }
			static HRESULT IsTestDll(const CPath&) { return S_OK; }
			static HRESULT RunTddTestsInDll(const CString&, TDD::Discriminator& pred, TDD::Reporter& func)
			{
				TDD::UnitTestInfo uti("class", "method");
				if (true == pred.WantTest(uti))
				{
					func.ForEachTest(uti);
					func.ForEachFailure(TDD::TestFailure(&uti, 123, "long-path\\file.cpp", "some error"));
					func.ForEachTest(uti);
				}
				return S_OK;
			}
		};

		TestsTreeViewT<TestRunner> ttv(staticText, errorText, enableMock, progressMock);

		DialogThread dialogThread;
		HWND dlg = dialogThread.StartDialog();
		ttv.Init(::GetDlgItem(dlg, IDC_TREEVIEW_BILL));

		LoadNotification& ln = ttv;
		ln.LoadTests(CPath(L"foo.dll"));

		// select all tests
		ttv.GetRootItem().Select();
		RunNotification& rn = ttv;
		rn.Run();

		TddAssert().AreEqual(RGB(0xFF, 0x00, 0x00), progressMock.m_color);
	}
};
}